package com.gc.ip;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;



@SuppressWarnings("serial")
public class JIPAddressTable extends JPanel
{
	private IPEditor ipEditor = null;
	
	private DefaultTableModel tableModel = null;
	private JTable table = null;
	
	private JButton addButton = null;
	private JButton modifyButton = null;
	private JButton removeButton = null;
	
	private JPopupMenu popupMenu = null;
	private JMenuItem addMenuItem = null;
	private JMenuItem modifyMenuItem = null;
	private JMenuItem removeMenuItem = null;
	private JMenuItem moveUpMenuItem = null;
	private JMenuItem moveDownMenuItem = null;
	
	public JIPAddressTable(IPEditor ipEditor, Vector<IPAddress> tableData)
	{
		this.ipEditor = ipEditor;
		
		initGUI();
		addListeners();
		setData(tableData);
		
		int[] emptyRows = new int[0];
		this.enableControls(emptyRows);
	}
	public JIPAddressTable(IPEditor ipEditor)
	{
		this(ipEditor, null);
	}
	
	private void initGUI()
	{
		Vector columnNames = new Vector();
		columnNames.add("IP Address");
		columnNames.add("Prefix Length");
		columnNames.add("Subnet Mask");
		// Table Model
		tableModel = new DefaultTableModel((Vector) null, (Vector) columnNames)
		{
			public boolean isCellEditable(int row, int column) 
			{
				return false;
			}
		};
		
		// Init table
		table = new JTable(tableModel);
		table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
		table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		
		int[] columnWidths = {200, 100, 200};
		for(int i = 0, columnCount = table.getColumnCount(); i < columnCount; i++)
		{
			TableColumn column = table.getColumnModel().getColumn(i);
			column.setPreferredWidth(columnWidths[i]);
		}
		
		addButton = new JButton("Add");
		modifyButton = new JButton("Modify");
		removeButton = new JButton("Remove");
		
		setButtonSize(addButton);
		setButtonSize(modifyButton);
		setButtonSize(removeButton);
		
		//init table pane
		JScrollPane tablePane = new JScrollPane();
		tablePane.getViewport().add(table);
		
		//init button pane
		JPanel buttonPane = new JPanel();
		buttonPane.setLayout(new GridBagLayout());
		GridBagConstraints gbc = new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
			GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0);
		
		buttonPane.add(addButton, gbc);
		gbc.gridy++;
		buttonPane.add(modifyButton, gbc);
		gbc.gridy++;
		buttonPane.add(removeButton, gbc);
		
		this.setLayout(new BorderLayout());
		this.add(tablePane, BorderLayout.CENTER);
		
		JPanel wrapper = new JPanel();
		wrapper.setLayout(new BorderLayout());
		wrapper.add(buttonPane, BorderLayout.NORTH);
		this.add(wrapper, BorderLayout.EAST);
		
		popupMenu = new JPopupMenu();
		addMenuItem = new JMenuItem("Add");
		modifyMenuItem = new JMenuItem("Modify");
		removeMenuItem = new JMenuItem("Remove");
		moveUpMenuItem = new JMenuItem("Move up");
		moveDownMenuItem = new JMenuItem("Move down");
		
		popupMenu.add(addMenuItem);
		popupMenu.add(modifyMenuItem);
		popupMenu.add(removeMenuItem);
		popupMenu.addSeparator();
		popupMenu.add(moveUpMenuItem);
		popupMenu.add(moveDownMenuItem);
	}
	
	private void setButtonSize(JButton button)
	{
		Dimension dim = button.getPreferredSize();
		dim.width = 80;
		button.setMinimumSize(dim);
		button.setMaximumSize(dim);
		button.setPreferredSize(dim);
	}
	private void addListeners()
	{
		addMenuItem.addActionListener(new AddActionListener());
		addButton.addActionListener(new AddActionListener());
		
		modifyMenuItem.addActionListener(new ModifyActionListener());
		modifyButton.addActionListener(new ModifyActionListener());
		
		removeMenuItem.addActionListener(new RemoveActionListener());
		removeButton.addActionListener(new RemoveActionListener());
		
		moveUpMenuItem.addActionListener(new MoveUpActionListener());
		moveDownMenuItem.addActionListener(new MoveDownActionListener());
		
		table.addMouseListener(new MouseAdapter()
		{
			public void mouseClicked(MouseEvent e)
			{
				if(!SwingUtilities.isRightMouseButton(e))
				{
					return;
				}
				popupMenu.show(table, e.getX(), e.getY());
			}
		});
		table.getSelectionModel().addListSelectionListener(new SelectionListener());
	}
	
	private class AddActionListener implements ActionListener
	{
		public void actionPerformed(ActionEvent e)
		{
			if(ipEditor == null)
			{
				return;
			}
			addRow(ipEditor.getIPAddress());
		}
	}
	private class ModifyActionListener implements ActionListener
	{
		public void actionPerformed(ActionEvent e)
		{
			if(ipEditor == null)
			{
				return;
			}
			int[] selRows = table.getSelectedRows();
			if(selRows == null || selRows.length != 1)
			{
				return;
			}
			modifyRow(selRows[0], ipEditor.getIPAddress());
		}
	}
	private class RemoveActionListener implements ActionListener
	{
		public void actionPerformed(ActionEvent e)
		{
			int[] selRows = table.getSelectedRows();
			if(selRows == null || selRows.length == 0)
			{
				return;
			}
			removeRows(selRows);
		}
	}
	
	private class MoveUpActionListener implements ActionListener
	{
		public void actionPerformed(ActionEvent e)
		{
			moveUpSelectedRows();
		}
	}
	
	private class MoveDownActionListener implements ActionListener
	{
		public void actionPerformed(ActionEvent e)
		{
			moveDownSelectedRows();
		}
	}
	
	private class SelectionListener implements ListSelectionListener
	{
		public void valueChanged(ListSelectionEvent e)
		{
			int[] selectedRows = table.getSelectedRows();
			enableControls(selectedRows);
			updateObservers(selectedRows);
		}
	}
	
	private void enableControls(int[] selectedRows)
	{
		modifyButton.setEnabled(selectedRows.length == 1);
		removeButton.setEnabled(selectedRows.length >= 1);
		
		modifyMenuItem.setEnabled(selectedRows.length == 1);
		removeMenuItem.setEnabled(selectedRows.length >= 1);
	}
	
	private void updateObservers(int[] selectedRows)
	{
		if(this.ipEditor == null)
		{
			return;
		}
		if(selectedRows.length > 0)
		{
			this.ipEditor.setIPAddress(this.getIPAddress(selectedRows[0]));
		}
	}
	
	private void moveUpRows(Vector dataVec, int[] rows)
	{
		for(int i = 0; i < rows.length; i++)
		{
			//1. not the first row
			//2. the row is not next to the previous row
			if((rows[i] != 0) && !(i > 0 && rows[i] - 1 == rows[i - 1]))
			{
				Collections.swap(dataVec, rows[i], rows[i] - 1);
				//Update the row index
				rows[i] = rows[i] - 1;
			}
		}
	}
	private void moveDownRows(Vector dataVec, int[] rows)
	{
		for(int i = rows.length - 1; i >= 0; i--)
		{
			//1. not the last row
			//2. the row is not next to the next row
			if((rows[i] != (dataVec.size() - 1)) && !(i < rows.length - 1 && rows[i] + 1 == rows[i + 1]))
			{
				//swap avoids the data copy of vector
				Collections.swap(dataVec, rows[i], rows[i] + 1);
				//Update the row index
				rows[i] = rows[i] + 1;
			}
		}
	}
	
	private Vector toRowData(IPAddress ipAddress)
	{
		Vector rowData = new Vector(tableModel.getColumnCount());
		
		//Column ip
		rowData.add(ipAddress.getStrAddr());
		
		//Column prefix length
		String prefixLength = "";
		if(ipAddress.getPrefixLength() >= 0)
		{
			prefixLength = String.valueOf(ipAddress.getPrefixLength());
		}
		rowData.add(prefixLength);

		//Column subnet mask
		String mask = "";
		IPType ipType = ipAddress.getIPType();
		if(ipType == IPType.IPv4)
		{
			mask = IPUtils.toIPv4MaskString(ipAddress.getPrefixLength());
		}
		else if(ipType == IPType.IPv6)
		{
			mask = IPUtils.toIPv6MaskString(ipAddress.getPrefixLength());
		}
		if(mask == null)
		{
			mask = "";
		}
		rowData.add(mask);
		return rowData;
	}
	
	private Vector toTableData(Vector<IPAddress> tableData)
	{
		Vector dataVec = new Vector();
		for(int i = 0, size = tableData.size(); i < size; i++)
		{
			dataVec.add(toRowData(tableData.get(i)));
		}
		return dataVec;
	}
	
	public void setData(Vector<IPAddress> tableData)
	{
		if(tableData == null || tableData.isEmpty())
		{
			return;
		}
		Vector dataVec = tableModel.getDataVector();
		dataVec.clear();
		dataVec.addAll(toTableData(tableData));
		tableModel.fireTableDataChanged();
	}
	public void addRow(IPAddress ipAddress)
	{
		if(ipAddress == null || !ipAddress.isValid())
		{
			return;
		}
		tableModel.getDataVector().add(toRowData(ipAddress));
		
		int rowIndex = tableModel.getDataVector().size() - 1;
		tableModel.fireTableRowsInserted(rowIndex, rowIndex);
	}
	public void removeRow(int row)
	{
		tableModel.getDataVector().remove(row);
		tableModel.fireTableDataChanged();
	}
	
	public void removeRows(int[] rows)
	{
		Vector dataVec = tableModel.getDataVector();
		for(int i = rows.length - 1; i >= 0; i--)
		{
			dataVec.remove(rows[i]);
		}
		tableModel.fireTableDataChanged();
	}
	
	public void modifyRow(int row, IPAddress ipAddress)
	{
		if(ipAddress == null || !ipAddress.isValid())
		{
			return;
		}
		tableModel.getDataVector().set(row, toRowData(ipAddress));
		tableModel.fireTableRowsUpdated(row, row);
	}
	
	public void moveUpSelectedRows()
	{
		int[] selRows = table.getSelectedRows();
		if(selRows == null || selRows.length == 0)
		{
			return;
		}
		//Move the rows in Table Model
		moveUpRows(tableModel.getDataVector(), selRows);
		tableModel.fireTableDataChanged();
		
		//Update the selection
		table.getSelectionModel().clearSelection();
		for(int row : selRows)
		{
			table.getSelectionModel().addSelectionInterval(row, row);
		}
	}
	public void moveDownSelectedRows()
	{
		int[] selRows = table.getSelectedRows();
		if(selRows == null || selRows.length == 0)
		{
			return;
		}
		//Move the rows in Table Model
		moveDownRows(tableModel.getDataVector(), selRows);
		tableModel.fireTableDataChanged();
		
		//Update the selection
		table.getSelectionModel().clearSelection();
		for(int row : selRows)
		{
			table.getSelectionModel().addSelectionInterval(row, row);
		}
	}
	
	public IPAddress getIPAddress(int row)
	{
		Vector dataVec = tableModel.getDataVector();
		if(row < 0 || row >= dataVec.size())
		{
			return null;
		}
		Vector data = (Vector)dataVec.get(row);
		
		String ipAddressStr = (String)data.get(0);
		
		int prefixLength = -1;
		String prefixStr = (String)data.get(1);
		if(prefixStr != null && !prefixStr.isEmpty())
		{
			try
			{
				prefixLength = Integer.parseInt(prefixStr);
			}
			catch(NumberFormatException e)
			{
				prefixLength = -1;
			}
		}
		return new IPAddress(ipAddressStr, prefixLength);
	}
	
	public List<IPAddress> getAllIPAddress()
	{
		Vector dataVec = tableModel.getDataVector();
		if(dataVec.isEmpty())
		{
			return null;
		}
		List<IPAddress> dataList = new ArrayList<IPAddress>(dataVec.size());
		for(int i = 0, size = dataVec.size(); i < size; i++)
		{
			dataList.add(getIPAddress(i));
		}
		return dataList;
	}
	
	@Override
	public void setEnabled(boolean enabled)
	{
		super.setEnabled(enabled);
		
		addButton.setEnabled(enabled);
		modifyButton.setEnabled(enabled);
		removeButton.setEnabled(enabled);
		
		addMenuItem.setEnabled(enabled);
		modifyMenuItem.setEnabled(enabled);
		removeMenuItem.setEnabled(enabled);
		
		moveUpMenuItem.setEnabled(enabled);
		moveDownMenuItem.setEnabled(enabled);
		
		if(enabled)
		{
			int[] selRows = table.getSelectedRows();
			enableControls(selRows);
		}
	}
	
	public static void main(String[] args)
	{
		JFrame frame = new JFrame("JIPAddressTableDemo");
		
		JIPAddressTable tableDemo = new JIPAddressTable(null);
		Vector<IPAddress> dataVec = new Vector<IPAddress>();
		dataVec.add(new IPAddress("101.2.2.0", 12));
		dataVec.add(new IPAddress("102.2.2.1"));
		dataVec.add(new IPAddress("103.2.2.2", 15));
		dataVec.add(new IPAddress("104.2.2.3", 32));
		dataVec.add(new IPAddress("105.2.2.4", 0));
		dataVec.add(new IPAddress("106.2.2.5", 1));
		tableDemo.setData(dataVec);
		
		frame.getContentPane().add(tableDemo);
		
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(600, 200));
        frame.pack();
        frame.setVisible(true);
	}
}
